package com.eastdigit.bpm.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import net.sf.json.JSONArray;
import net.sf.json.JSONObject;

import org.activiti.engine.FormService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.ManagementService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.NativeHistoricProcessInstanceQuery;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.NativeDeploymentQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.eastdigit.bpm.cmd.DeleteProcessInstanceAndHisCmd;
import com.eastdigit.bpm.model.FormEntity;
import com.eastdigit.bpm.util.BpmUtil;
import com.eastdigit.data.Constant;
import com.eastdigit.system.model.BusinessException;
import com.eastdigit.servlet.ReqBean;
import com.eastdigit.system.service.BaseService;
import com.eastdigit.util.DateUtil;
import com.eastdigit.util.StringUtil;

/**
 * Created by Administrator on 2015/9/21.
 */
@Service
public class ProcessService extends BaseService {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private FormService formService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    protected RuntimeService runtimeService;
    @Autowired
    protected HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private ManagementService managementService;

    /**
     * 流程定义查询
     * 
     * @param reqBean
     * @return
     */
    public JSONArray getProcessDefinitions(ReqBean reqBean) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        if (reqBean.containsKey("processDefinitionId")) {
            processDefinitionQuery.processDefinitionId(reqBean.getInfo("processDefinitionId"));
        }
        if (reqBean.containsKey("processDefinitionKey")) {
            processDefinitionQuery.processDefinitionKey(reqBean.getInfo("processDefinitionKey"));
        }
        if (reqBean.containsKey("processDefinitionName")) {
            processDefinitionQuery.processDefinitionName(reqBean.getInfo("processDefinitionName"));
        }
        if (reqBean.containsKey("deployCategory")) {
            String sql = "select * from ACT_RE_DEPLOYMENT ard where ard.CATEGORY_ like '"
                    + reqBean.getInfo("deployCategory") + "%'";
            NativeDeploymentQuery query = repositoryService.createNativeDeploymentQuery();
            List<Deployment> list = new ArrayList<Deployment>();
            list = query.sql(sql).list();
            Set<String> deploymentIds = new HashSet<String>();
            if (list != null && list.size() > 0) {
                for (Deployment d : list) {
                    deploymentIds.add(d.getId());
                }
            }
            processDefinitionQuery.deploymentIds(deploymentIds);
        }
        processDefinitionQuery.latestVersion();
        processDefinitionQuery.active();
        processDefinitionQuery.orderByProcessDefinitionName().asc();
        List<ProcessDefinition> list = processDefinitionQuery.list();

        JSONArray array = new JSONArray();
        for (ProcessDefinition processDefinition : list) {
            array.add(BpmUtil.toJSONObject(processDefinition));
        }

        return array;
    }

    /**
     * 删除部署 注：删除所有相关流程数据-谨慎调用
     * 
     * @param deploymentId
     * @return
     * @throws Exception
     */
    public void deleteDeployment(String deploymentId) {
        repositoryService.deleteDeployment(deploymentId, true);//
    }

    /**
     * 删除流程定义(删除key相同的所有不同版本的流程定义)
     * 
     * @param processDefinitionKey
     */
    public void deleteProcessDefinitionByKey(String processDefinitionKey) {
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
                processDefinitionKey)// 使用流程定义的key查询
                .list();
        if (list != null && list.size() > 0) {
            for (ProcessDefinition pd : list) {
                String deploymentId = pd.getDeploymentId();
                repositoryService.deleteDeployment(deploymentId, true);
            }
        }
    }

    /**
     * 流程实例查询
     * 
     * @param reqBean
     * @return
     */
    public JSONObject getProcessInstances(ReqBean reqBean) {
        NativeHistoricProcessInstanceQuery nativeHistoricProcessInstanceQuery = historyService
                .createNativeHistoricProcessInstanceQuery();
        StringBuffer sqlList = new StringBuffer("select RES.* ");
        StringBuffer sqlCount = new StringBuffer("select count(distinct RES.ID_) ");
        StringBuffer sql = new StringBuffer(
                " from ACT_HI_PROCINST RES , ACT_RE_PROCDEF DEF where RES.PROC_DEF_ID_ = DEF.ID_ ");

        String processDefinitionKey = reqBean.getInfo("processDefinitionKey");
        if (!StringUtil.isEmpty(processDefinitionKey)) {
            sql.append(" and DEF.KEY_ = #{processDefinitionKey}");
            nativeHistoricProcessInstanceQuery.parameter("processDefinitionKey", processDefinitionKey);
        }

        String processInstanceId = reqBean.getInfo("processInstanceId");
        if (!StringUtil.isEmpty(processInstanceId)) {
            sql.append(" and RES.PROC_INST_ID_ = #{processInstanceId}");
            nativeHistoricProcessInstanceQuery.parameter("processInstanceId", processInstanceId);
        }

        String starter = reqBean.getInfo("starter");
        if (!StringUtil.isEmpty(starter)) {
            sql.append(" and RES.START_USER_ID_ = #{startedBy}");
            nativeHistoricProcessInstanceQuery.parameter("startedBy", starter);
        }

        String processName = reqBean.getInfo("processName");
        if (!StringUtil.isEmpty(processName)) {
            sql.append(" and RES.NAME_ like #{nameLike}");
            nativeHistoricProcessInstanceQuery.parameter("nameLike", processName);
        }

        String starteTimeAfter = reqBean.getInfo("startTimeAfter");
        if (!StringUtil.isEmpty(starteTimeAfter)) {
            sql.append(" and RES.START_TIME_ >= #{startTimeAfter, jdbcType=TIMESTAMP}");
            nativeHistoricProcessInstanceQuery.parameter("startTimeAfter", DateUtil.parseDateTime(starteTimeAfter));
        }

        String startTimeBefore = reqBean.getInfo("startTimeBefore");
        if (!StringUtil.isEmpty(startTimeBefore)) {
            sql.append(" and RES.START_TIME_ < #{startTimeBefore, jdbcType=TIMESTAMP}");
            nativeHistoricProcessInstanceQuery.parameter("startTimeBefore", DateUtil.addDay(DateUtil
                    .parse(startTimeBefore), 1));
        }

        if (reqBean.containsKey("finished")) {
            sql.append(" and RES.END_TIME_ is " + (Boolean.parseBoolean(reqBean.getInfo("finished")) ? "not" : "")
                    + " null");
        }

        String assignee = reqBean.getInfo("assignee");
        if (!StringUtil.isEmpty(assignee)) {
            sql.append(" and exists (");
            sql.append("select 1 from ACT_HI_TASKINST T where RES.PROC_INST_ID_ = T.PROC_INST_ID_");
            sql.append(" and T.ASSIGNEE_ = #{assignee}");
            String taskDefinitionKey = reqBean.getInfo("taskDefinitionKey");
            if (!StringUtil.isEmpty(taskDefinitionKey)) {
                sql.append(" and T.TASK_DEF_KEY_ = #{taskDefinitionKey}");
                nativeHistoricProcessInstanceQuery.parameter("taskDefinitionKey", taskDefinitionKey);
            }
            sql.append(")");
            nativeHistoricProcessInstanceQuery.parameter("assignee", assignee);
        }

        sql.append(" order by RES.START_TIME_ desc ");

        nativeHistoricProcessInstanceQuery.sql(sqlList.append(sql).toString());

        int pageSize = reqBean.getInfoInt(Constant.PAGE_SIZE);
        pageSize = pageSize <= 0 ? 10 : pageSize;
        int start = reqBean.getInfoInt(Constant.PAGE_INDEX) * pageSize;
        List<HistoricProcessInstance> list = nativeHistoricProcessInstanceQuery.listPage(start, pageSize);
        JSONArray array = new JSONArray();
        for (HistoricProcessInstance historicProcessInstance : list) {
            JSONObject j = BpmUtil.toJSONObject(historicProcessInstance);
            JSONObject form = new JSONObject();
            if (!StringUtil.isEmpty(historicProcessInstance.getBusinessKey())) {
                form = FormFactory.getFormData(historicProcessInstance.getBusinessKey());
            }
            j.put("form", form);

            ProcessDefinition pd = repositoryService.getProcessDefinition(historicProcessInstance
                    .getProcessDefinitionId());
            j.put("processDefinitionName", pd.getName());
            array.add(j);
        }

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("rows", array);
        jsonObject.put("total", nativeHistoricProcessInstanceQuery.sql(sqlCount.append(sql).toString()).count());

        return jsonObject;
    }

    /**
     * 初始启动表单数据
     * 
     * @param processDefinitionKey
     * @return
     * @throws Exception
     */
    public JSONObject getStartForm(String processDefinitionKey, ReqBean reqBean) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
                processDefinitionKey).latestVersion().singleResult();
        JSONObject startForm = FormFactory.getForm(formService.getStartFormData(processDefinition.getId()));
        startForm.put("processDefinitionKey", processDefinition.getKey());
        startForm.put("processDefinition", BpmUtil.toJSONObject(processDefinition));
        
        String serviceName = reqBean.getInfo("_service");
        String businessId = reqBean.getInfo("businessId");
        if(!StringUtil.isEmpty(businessId) && !StringUtil.isEmpty(serviceName)){
            startForm.put("form", FormFactory.getFormData(FormFactory.buildBussinessKey(serviceName, businessId)));
        }
        return startForm;
    }
    
    /**
     * 提交业务数据,启动流程
     * 
     * @param processDefinitionKey
     * @param title
     * @param jsonProperties
     */
    public void submitAndStartProcessByKey(String processDefinitionKey, String title, String jsonProperties, String starter) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
                processDefinitionKey).latestVersion().singleResult();
        if (processDefinition == null) {
            throw new BusinessException("未发现流程定义[key:" + processDefinitionKey + "]!");
        }
        this.submitAndStartProcessById(processDefinition.getId(), title, jsonProperties, starter);

    }

    /**
     * 提交业务数据,启动流程
     * 
     * @param processDefinitionId
     * @param title
     * @param jsonProperties
     */
    public void submitAndStartProcessById(String processDefinitionId, String title, String jsonProperties, String starter) {
        Map<String, String> startFormProperties = FormFactory.getProperties(jsonProperties);
        String businessId = "";
        try {
            if(startFormProperties.containsKey("businessId") && !StringUtil.isEmpty(startFormProperties.get("businessId"))){
                businessId = startFormProperties.get("businessId");
                FormFactory.getFormService(startFormProperties).update(startFormProperties);
            }else{
                FormEntity entity = FormFactory.getFormService(startFormProperties).add(startFormProperties);
                businessId = entity.getId();
            }
        } catch (Exception e) {
            throw new BusinessException("提交业务数据错误:" + e.getMessage());
        }
        String businessKey = FormFactory.buildBussinessKey(FormFactory.getServiceName(startFormProperties),businessId);
        this.startProcess(processDefinitionId, title, businessKey, startFormProperties, starter);

    }
    
    

    /**
     * 启动流程
     * 
     * @param processDefinitionKey
     * @param title
     * @param jsonProperties
     */
    public void startProcessByKey(String processDefinitionKey, String title, String jsonProperties, String starter) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
                processDefinitionKey).latestVersion().singleResult();
        if (processDefinition == null) {
            throw new BusinessException("未发现流程定义[key:" + processDefinitionKey + "]!");
        }
        Map<String, String> startFormProperties = FormFactory.getProperties(jsonProperties);
        String businessId = startFormProperties.get("id");
        if (StringUtil.isEmpty(businessId)) {
            throw new BusinessException("流程启动参数businessId为空!");
        }
        String businessKey = FormFactory.buildBussinessKey(FormFactory.getServiceName(startFormProperties),businessId);
        this.startProcess(processDefinition.getId(), title, businessKey, startFormProperties, starter);

    }
    
    /**
     * 启动流程
     * 
     * @param processDefinitionKey
     * @param title
     * @param jsonProperties
     */
    public void startProcessByKey(String processDefinitionKey, String title, String serviceName, FormEntity startFormEntry, String starter) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(
                processDefinitionKey).latestVersion().singleResult();
        if (processDefinition == null) {
            throw new BusinessException("未发现流程定义[key:" + processDefinitionKey + "]!");
        }
        if (startFormEntry == null || StringUtil.isEmpty(startFormEntry.getId())) {
            throw new BusinessException("流程启动参数startFormEntry为空!");
        }
        Map<String, String> startFormProperties = FormFactory.getProperties(startFormEntry.toJSONObject().toString());
        String businessKey = FormFactory.buildBussinessKey(serviceName,startFormEntry.getId());
        this.startProcess(processDefinition.getId(), title, businessKey, startFormProperties, starter);

    }

    /**
     * 启动流程
     * 
     * @param processDefinitionId
     * @param title
     * @param properties
     */
    private synchronized void startProcess(String processDefinitionId, String title, String businessKey,
            Map<String, String> startFormProperties, String starter) {
        ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
        if (processDefinition == null) {
            throw new BusinessException("未发现流程定义[id:" + processDefinitionId + "]!");
        }
        try {
            identityService.setAuthenticatedUserId(starter);

            ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinitionId, businessKey,
                    new HashMap<String, Object>(startFormProperties));

            // 设置流程名称
            if (!StringUtil.isEmpty(title)) {
                runtimeService.setProcessInstanceName(processInstance.getId(), title);
            }
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        } finally {
            identityService.setAuthenticatedUserId(null);
        }
    }

    /**
     * 删除流程实例
     * 
     * @param processInstanceId
     */
    public void deleteProcess(String processInstanceId, boolean deleteBusiness) throws Exception {
        String businessKey = "";
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(
                processInstanceId).singleResult();
        if (processInstance != null) {
            businessKey = processInstance.getBusinessKey();
        } else {
            HistoricProcessInstance historyProcessInstance = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(processInstanceId).singleResult();
            businessKey = historyProcessInstance.getBusinessKey();
        }
        managementService.executeCommand(new DeleteProcessInstanceAndHisCmd(processInstanceId, ""));

        if (deleteBusiness && !StringUtil.isEmpty(businessKey)) {
            IFormService<FormEntity> service = FormFactory.getFormServiceByKey(businessKey);
            String businessId = FormFactory.getBusinessId(businessKey);
            if (service != null && !StringUtil.isEmpty(businessId)) {
                service.delete(businessId);
            }
        }

    }

    /**
     * 获取流程实例相关信息
     * @param processInstanceId
     * @return
     */
    public JSONObject getProcessInfo(String processInstanceId) {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        JSONObject processInfo = FormFactory.getForm(formService.getStartFormData(processInstance
                .getProcessDefinitionId()));
        processInfo.put("form", FormFactory.getFormData(processInstance.getBusinessKey()));
        processInfo.put("comments", this.getProcessComments(processInstanceId));

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(
                processInstance.getProcessDefinitionId()).singleResult();
        processInfo.put("processDefinition", BpmUtil.toJSONObject(processDefinition));

        return processInfo;
    }

    /**
     * 查询流程实例轨迹
     * 
     * @param processInstanceId
     * @return
     */
    public JSONArray getProcessTracks(String processInstanceId) {
        HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
        JSONArray array = new JSONArray();
        JSONArray comments = getProcessComments(processInstanceId);
        for (HistoricActivityInstance his : historicActivityInstances) {
            if ("startEvent".equals(his.getActivityType()) || "userTask".equals(his.getActivityType())
                    || "endEvent".equals(his.getActivityType())) {

                JSONObject hisObj = new JSONObject();
                hisObj.put("processInstanceId", processInstanceId);
                hisObj.put("startUser", StringUtil.noNull(processInstance.getStartUserId(), ""));
                hisObj.put("executionId", StringUtil.noNull(his.getExecutionId(), ""));
                hisObj.put("taskId", StringUtil.noNull(his.getTaskId(), ""));
                hisObj.put("actName", StringUtil.noNull(his.getActivityName(), ("startEvent".equals(his
                        .getActivityType()) ? "开始" : ("endEvent".equals(his.getActivityType()) ? "结束" : ""))));
                hisObj.put("actType", StringUtil.noNull(his.getActivityType(), ""));
                hisObj.put("assignee", "startEvent".equals(his.getActivityType()) ? StringUtil.noNull(processInstance
                        .getStartUserId(), "") : StringUtil.noNull(his.getAssignee(), ""));
                hisObj.put("startTime", StringUtil.noNull(DateUtil.formatDateTime(his.getStartTime()), ""));
                hisObj.put("endTime", StringUtil.noNull(DateUtil.formatDateTime(his.getEndTime()), ""));

                if ("userTask".equals(his.getActivityType()) && !StringUtil.isEmpty(his.getTaskId())
                        && !comments.isEmpty()) {
                    String commentMsg = "";
                    for (Object comment : comments) {
                        JSONObject json = (JSONObject) comment;
                        if (json.containsKey("taskId") && his.getTaskId().equals(json.getString("taskId"))
                                && json.containsKey("message")) {
                            commentMsg = commentMsg + (StringUtil.isEmpty(commentMsg) ? "" : ",")
                                    + json.getString("message");
                        }
                    }
                    hisObj.put("comment", StringUtil.noNull(commentMsg, ""));
                } else {
                    hisObj.put("comment", "");
                }

                array.add(hisObj);
            }
        }
        return array;
    }

    /**
     * 审批意见
     * 
     * @param taskId
     * @return
     */
    public JSONArray getProcessComments(String processInstanceId) {
        JSONArray jsonArray = new JSONArray();
        List<Comment> comments = new ArrayList<Comment>();
        if (!StringUtil.isEmpty(processInstanceId)) {
            comments = taskService.getProcessInstanceComments(processInstanceId);
            for (int i = comments.size() - 1; i >= 0; i--) {
                Comment comm = comments.get(i);
                JSONObject json = new JSONObject();
                json.put("taskId", comm.getTaskId());
                json.put("processInstanceId", comm.getProcessInstanceId());
                json.put("commentTime", DateUtil.formatDateTime(comm.getTime()));
                json.put("message", comm.getFullMessage());
                json.put("type", comm.getType());
                json.put("userId", StringUtil.noNull(comm.getUserId(), ""));
                jsonArray.add(json);
            }
        }
        return jsonArray;
    }

}
